from jvm.class_reader import ClassReader
from jvm.constant_pool import *
from jvm.member import MemberEngine, AttributeEngine


class ClassReaderEngine:

    def __init__(self, byte_codes):
        # 主次版本号
        self.minorVersion = None
        self.majorVersion = None
        # 常量池
        self.constantPool = []
        # 类访问标志
        self.accessFlags = None
        # 类和超类索引
        self.thisClass = None
        self.superClass = None
        # 接口索引表(多个接口)
        self.interfaces = []
        # 字段表信息
        self.fields = []
        # 方法表信息
        self.methods = []
        # 属性信息
        self.attributes = []
        # ClassReader工具
        self.classReader = ClassReader(byte_codes)

    def reader(self):
        # 读取 魔数
        self.read_magic()
        # 读取主次版本号
        self.read_version()
        # 读取常量池
        self.read_constant_pool()
        # 常量池之后是类访问标志，这是一个16位的“bitmask”，指出class文件定义的是类还是接口，访问级别是public还是private，等等
        self.accessFlags = self.classReader.read_u2()
        self.thisClass = self.classReader.read_u2()
        # 其值指向常量池中对应的索引值
        self.superClass = self.classReader.read_u2()
        self.interfaces = self.classReader.read_u2s()

        # 解析类的字段 和方法
        self.fields = MemberEngine.read_members(self.classReader, self.constantPool)
        # 用统一个方法解析的原因：方法各个数据项的含义非常相似，仅在访问标志位和属性表集合的可选项上有略微不同
        self.methods = MemberEngine.read_members(self.classReader, self.constantPool)
        # 解析类的属性
        AttributeEngine.read_attributes(self.classReader, self.constantPool)

        return self

    def read_magic(self):
        # 读取 4个字节  得到 魔数
        magic = self.classReader.read_u4()
        print('魔数:', hex(magic))

    def read_version(self):
        # 读取 2个字节 得到主，次版本号
        self.minorVersion = self.classReader.read_u2()
        self.majorVersion = self.classReader.read_u2()

    # 读取常量池。 要注意下面三点:
    # 第一，表头给出的常量池大小比实际大1。假设表头给出的值是n，那么常量池的实际大小是n–1。
    # 第二，有效的常量池索引是1~n–1。0是无效索引，表示不指向任何常量。
    # 第三，CONSTANT_Long_info和CONSTANT_Double_info各占两个位置。
    def read_constant_pool(self):
        # 先读取常量池的长度
        len_pool = self.classReader.read_u2()
        index = 0
        while True:
            if index >= len_pool:
                break
            if index == 0:  # 索引从1开始
                self.constantPool.append(None)
                index = index + 1
                continue
            # 根据tag 构造具体哪个常量
            constant_obj = create_constant_info(self.classReader)
            # 自我解析常量
            constant_obj.read(self.classReader)
            # 将常量 添加到常量池
            self.constantPool.append(constant_obj)
            # CONSTANT_Long_info和CONSTANT_Double_info各占两个位置
            # 常量池中存在这种占两个位置的常量，将会有更多的无效索引，实际的常量数量会比n-1还少
            if isinstance(constant_obj, ConstantLongInfo) or isinstance(constant_obj, ConstantDoubleInfo):
                self.constantPool.append(None)
                index = index + 1
            index = index + 1

    def to_string(self):
        print("解析到的class信息如下:")
        print('主.次版本:\t\t', str(self.majorVersion) + "." + str(self.minorVersion))

        # 00 21可以推断是由ACC_PUBLIC和ACC_SUPER通过OR组合而成。
        print('accessFlags：\t\t', hex(self.accessFlags))
        # # 打印本类名称，先找到对应的 ConstantClassInfo表 ， 然后 去 ConstantUtf8Info 字符串常量池表中找
        print(type(self.thisClass))
        class_index = self.constantPool[self.thisClass].get_index()
        class_name = self.constantPool[class_index].get_val()
        print('thisClass：\t\t', class_name)
        class_index = self.constantPool[self.superClass].get_index()
        class_name = self.constantPool[class_index].get_val()
        print('superClass：\t\t', class_name)
        # 接口数量
        class_name = None
        for i in self.interfaces:
            class_index = self.constantPool[i].get_index()
            class_name = self.constantPool[class_index].get_val()
        print('interfaces：\t\t', class_name)
        constants_str = '['
        index = 0
        for item in self.constantPool:
            constants_str = constants_str + str(index) + '\t' + item.__class__.__name__ + '\t'
            if item is not None:
                constants_str = constants_str + '# ' + str(item.get_val())
            constants_str = constants_str + ",\r\n"
            index = index + 1
        print('常量池信息：\r\n', constants_str + ']')

        # 打印字段信息
        for i in range(0, len(self.fields)):
            member = self.fields[i]
            print("第", (i + 1), "个field信息如下:")
            print("\t字段名称: ", self.constantPool[member.nameIndex].val)
            print("\t字段accessFlags: ", member.accessFlags)
            print("\t字段descriptor: ", self.constantPool[member.descriptorIndex].val)

        # 打印方法信息
        for i in range(0, len(self.methods)):
            method = self.methods[i]
            print("第", i, "个method信息如下:")
            print("\t方法名称: ", self.constantPool[method.nameIndex].val)
            print("\t方法accessFlags: ", method.accessFlags)
            print("\t方法descriptor: ", self.constantPool[method.descriptorIndex].val)
